/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufba.lasid.ds.replication;

import br.ufba.lasid.base.api.IResource;
import br.ufba.lasid.base.api.IResource.States;
import br.ufba.lasid.comm.Hosttable;
import br.ufba.lasid.comm.api.ICommunicator;
import br.ufba.lasid.comm.api.IHost;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager.MessageKinds;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager.ModuleTypes;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceMessageHandler;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author aliriosa
 */
public class ReplicatedResourceMessageHandler implements IReplicatedResourceMessageHandler {

    IReplicatedResourceManager replicator;
    private static final Log logger = LogFactory.getLog(ReplicatedResourceMessageHandler.class);

    public ReplicatedResourceMessageHandler() {
    }

    public ReplicatedResourceMessageHandler(IReplicatedResourceManager replicator) {
        this.replicator = replicator;
    }

    @Override
    public void handle(String hostID, String message) throws Exception {

        //logger.info(message + " from " + hostID + " has been received.");

        MessageKinds msgKind = IReplicatedResourceManager.Utils.getMessageType(message);

//        logger.info(" message kind " + msgKind);

        switch (msgKind) {
            case StateReport:
                // handleReporting(hostID, message);
                break;
            case CheckState:
                handleChecking(hostID, message);
        }

    }

    public void handleChecking(String hostID, String request) throws Exception {

        IHost localhost = replicator.getLocalhost();

        if (localhost.getID().equals(hostID)) {
            return;
        }

        ICommunicator comm = replicator.getCommunicator();

        /**
         * We use the message as a opportunity to update the host state.
         */
        Hosttable hosttable = replicator.getHosttable();
        IHost host = hosttable.get(hostID);

        if (host != null) {
            host.setState(States.Up);
        }

        /**
         * Here, we'll send a response for the check state message.
         */
        String response = IReplicatedResourceManager.Utils.newMessage(
                MessageKinds.StateReport, localhost.getResourcetable());

        /**
         * Instantiate a new thread at each sending instant can bring some
         * performance issues -- we must review it later.
         */
        new Sender(host, response).start();
        //comm.send(hostID, detector.getName(), response);



    }

    @Override
    public ModuleTypes getModuleType() throws Exception {
        return ModuleTypes.MessageHandler;
    }

    @Override
    public void setReplicatedResource(IReplicatedResourceManager replicator) throws Exception {
        this.replicator = replicator;
    }

    @Override
    public void startup() throws Exception {
        new Thread(this).start();
    }

    @Override
    public void run() {
        //do nothing
    }

    @Override
    public String getName() throws Exception {
        return getModuleType().name();
    }

    @Override
    public void receive(String host, String component, String message) throws Exception {
        if (component != null && component.equals(replicator.getName())) {
            handle(host, message);
        }
    }

    class Sender extends Thread {

        IHost host;
        String message;

        public Sender() {
        }

        public Sender(IHost host, String message) {
            this.host = host;
            this.message = message;
        }

        @Override
        public void run() {
            String hostID = host.getID();
            try {
                replicator.getCommunicator().send(hostID, replicator.getName(), message);
            } catch (ConnectException ce) {
                host.setState(IResource.States.Down);
                //ce.printStackTrace();
            } catch (NoRouteToHostException nre) {
                host.setState(IResource.States.Down);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
